package com.wei.czz.framework.common.manager;

import com.wei.czz.common.dto.common.fileFlow.FileFlowDto;
import com.wei.czz.common.dto.common.fileFlow.FilePreUploadDto;
import com.wei.czz.common.dto.common.fileFlow.FileUploadDto;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.ResultEnum;
import com.wei.czz.common.enums.common.dict.DictEnum;
import com.wei.czz.common.enums.common.multiDIct.MultiDictEnum;
import com.wei.czz.common.exception.CzzException;
import com.wei.czz.common.property.MinioProperty;
import com.wei.czz.common.tool.PageDto;
import com.wei.czz.common.utils.CopyUtils;
import com.wei.czz.common.utils.ShaUtils;
import com.wei.czz.common.vo.common.fileFlow.*;
import com.wei.czz.framework.common.entity.DictEntity;
import com.wei.czz.framework.common.entity.FileChunkFlowEntity;
import com.wei.czz.framework.common.entity.FileFlowEntity;
import com.wei.czz.framework.common.entity.MultiDictEntity;
import com.wei.czz.framework.common.wrapper.MinioWrapper;
import com.wei.czz.framework.common.helper.MultiDictHelper;
import com.wei.czz.framework.common.service.FileChunkFlowService;
import com.wei.czz.framework.common.service.FileFlowService;
import com.wei.czz.framework.common.wrapper.DictWrapper;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wyw
 * date: 2025-03-28 12:02:56
 * className: FileManager
 * version: 1.0
 * description:
 */
@Component
@AllArgsConstructor
public class FileFlowManager {

    private static final Logger log = LoggerFactory.getLogger(FileFlowManager.class);

    private final MinioProperty minioProperty;

    private final FileFlowService fileFlowService;

    private final FileChunkFlowService fileChunkFlowService;

    private final MinioWrapper minioWrapper;

    private final MultiDictHelper multiDictHelper;

    private final DictWrapper dictWrapper;

    /**
     * 文件上传
     *
     * @param fileUploadVo 文件上传参数对象
     * @return 结果
     */
    public FileUploadDto fileUpload(FileUploadVo fileUploadVo) {

        String fileHash = fileUploadVo.getFileHash();
        MultipartFile file = fileUploadVo.getFile();
        log.info("文件名称：{} 文件大小：{} 文件类型：{}", file.getOriginalFilename(), file.getSize(), file.getContentType());

        // 计算文件哈希值
        String _fileHash = ShaUtils.encodeSha256(file);
        log.info("文件哈希值：{}", _fileHash);
        if (!fileHash.equals(_fileHash)) {
            log.info("文件哈希值校验失败");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "文件哈希值对比不一致，请确认后再上传");
        }

        /*
            上传文件到minio
         */
        Long fileNo = minioWrapper.uploadFile(fileHash, file);

        // 判断文件是否是图片
        boolean isImage = minioWrapper.isImage(file.getContentType());
        String viewUrl = StringUtils.EMPTY;
        String thumbViewUrl = StringUtils.EMPTY;
        String downloadUrl;

        if (isImage) {
            // 获取图片预览链接
            viewUrl = minioWrapper.buildPreviewUrl(fileNo, 30, TimeUnit.MINUTES);
            // 获取图片缩略图预览链接
            thumbViewUrl = minioWrapper.buildThumbPreviewUrl(fileNo, 30, TimeUnit.MINUTES);
        }

        // 获取图片下载链接
        downloadUrl = minioWrapper.buildDownloadUrl(fileNo, 1, TimeUnit.DAYS);

        FileUploadDto fileUploadDto = new FileUploadDto();
        fileUploadDto.setFileNo(fileNo)
                .setIsImage(isImage ? CommonEnum.ZERO.getValue() : CommonEnum.ONE.getValue())
                .setViewUrl(viewUrl)
                .setThumbViewUrl(thumbViewUrl)
                .setDownloadUrl(downloadUrl);
        return fileUploadDto;
    }

    /**
     * 大文件分片上传
     *
     * @param uploadVo 大文件分片上传参数对象
     */
    public void chunkFileUpload(ChunkFileUploadVo uploadVo) {

        String fileHash = uploadVo.getFileHash();
        String chunkFileHash = uploadVo.getChunkFileHash();
        MultipartFile file = uploadVo.getFile();
        log.info("文件名称：{} 文件大小：{}", file.getOriginalFilename(), file.getSize());

        // 计算文件哈希值
        String _fileHash = ShaUtils.encodeSha256(file);
        log.info("切片文件哈希值：{}", _fileHash);
        if (!chunkFileHash.equals(_fileHash)) {
            log.info("切片文件哈希值校验失败");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "切片文件哈希值对比不一致，请确认后再上传");
        }

        /*
            上传文件到minio
         */
        minioWrapper.uploadChunkFile(fileHash, chunkFileHash, uploadVo.getChunkSerial(), file);

    }

    /**
     * 文件预上传
     *
     * @param preUploadVo 文件预上传参数对象
     * @return 结果
     */
    public FilePreUploadDto filePreUpload(FilePreUploadVo preUploadVo) {

        String fileHash = preUploadVo.getFileHash();
        String fileName = preUploadVo.getFileName();
        long fileSize = preUploadVo.getFileSize();
        log.info("文件哈希值：{} 文件名称：{} 文件大小：{}", fileHash, fileName, fileSize);

        /*
            获取文件上传记录
         */
        FileFlowEntity fileFlow = minioWrapper.getFileFlow(fileHash);
        if (Objects.nonNull(fileFlow)) {
            log.info("文件上传记录已存在");
            /*
                复制文件上传记录
             */
            Long fileNo = fileFlow.getFileNo();
            if (!fileName.equals(fileFlow.getOriginalFileName())) {
                // 文件名称不一致，复制文件上传记录
                fileNo = fileFlowService.copy(fileFlow.getId(), fileName);
            }

            FilePreUploadDto createDto = new FilePreUploadDto();
            createDto.setFileNo(fileNo);
            return createDto;
        }

        /*
            获取分片大小字典配置
         */
        DictEntity dict = dictWrapper.getOneDict(DictEnum.CHUNK_FILE_SIZE);

        long chunkFileSize;
        try {
            chunkFileSize = Long.parseLong(dict.getValue());
        } catch (NumberFormatException e) {
            log.error("字典值配置错误。dice={} {}", DictEnum.CHUNK_FILE_SIZE.getName(), dict);
            throw new CzzException();
        }

        if (fileSize <= chunkFileSize) {
            log.info("文件大小小于分片大小，直接上传");
            FilePreUploadDto createDto = new FilePreUploadDto();
            createDto.setIsNeedChunk(CommonEnum.ONE.getValue());
            return createDto;
        }

        /*
            获取切片文件上传记录
         */
        List<FileChunkFlowEntity> fileChunkFlowList = fileChunkFlowService.findList(Collections.singletonList(fileHash));
        // 获取切片序号集合
        Set<Integer> chunkSerialSet = CopyUtils.mapSet(fileChunkFlowList, FileChunkFlowEntity::getChunkSerial);

        // 计算分片数量
        int chunkCount = (int) Math.ceil((double) fileSize / chunkFileSize);
        log.info("分片数量：{}", chunkCount);

        List<Integer> chunkSerialList = new ArrayList<>();
        for (int i = 0; i < chunkCount; i++) {
            if (!chunkSerialSet.contains(i)) {
                chunkSerialList.add(i);
            }
        }

        FilePreUploadDto createDto = new FilePreUploadDto();
        createDto.setIsNeedChunk(CommonEnum.ZERO.getValue())
                .setChunkCount(chunkCount)
                .setChunkSize(chunkFileSize)
                .setChunkSerialList(chunkSerialList);
        return createDto;
    }

    public Long getChunkFileSize() {

        /*
            获取分片大小字典配置
         */
        DictEntity dict = dictWrapper.getOneDict(DictEnum.CHUNK_FILE_SIZE);

        long chunkFileSize;
        try {
            chunkFileSize = Long.parseLong(dict.getValue());
        } catch (NumberFormatException e) {
            log.error("字典值配置错误。dice={} {}", DictEnum.CHUNK_FILE_SIZE.getName(), dict);
            throw new CzzException();
        }

        return chunkFileSize;
    }

    public PageDto<FileFlowDto> getFileFlowPageList(FileFlowFormVo formVo) {

        /*
            分页获取文件上传记录
         */
        PageDto<FileFlowEntity> pageDto = fileFlowService.getFileFlowPageList(formVo);
        if (pageDto.isEmpty()) {
            log.info("分页查询文件上传记录为空");
            return pageDto.rebuild();
        }

        List<FileFlowEntity> fileFlowList = pageDto.getList();
        // 列表映射
        Function<FileFlowEntity, FileFlowDto> function = fileFlow -> {
            // 对象映射
            FileFlowDto fileFlowDto = CopyUtils.map(fileFlow, FileFlowDto.class);

            // 判断文件是否是图片
            boolean isImage = minioWrapper.isImage(fileFlow.getFileType());
            fileFlowDto.setIsImage(isImage ? CommonEnum.ZERO.getValue() : CommonEnum.ONE.getValue());

            return fileFlowDto;
        };
        List<FileFlowDto> fileFlowDtoList = CopyUtils.mapList(fileFlowList, function);

        // 封装返回
        return pageDto.rebuild(fileFlowDtoList);
    }

    /**
     * 构建文件
     *
     * @param fileNo 文件编号
     * @return 结果
     */
    public FileUploadDto buildFile(Long fileNo) {

        /*
            获取文件上传记录
         */
        FileFlowEntity fileFlow = minioWrapper.getFileFlow(fileNo);

        // 判断文件是否是图片
        boolean isImage = minioWrapper.isImage(fileFlow.getFileType());
        String viewUrl = StringUtils.EMPTY;
        String thumbViewUrl = StringUtils.EMPTY;
        String downloadUrl;

        if (isImage) {
            // 获取图片预览链接
            viewUrl = minioWrapper.buildPreviewUrl(fileFlow, 30, TimeUnit.MINUTES);
            // 获取图片缩略图预览链接
            thumbViewUrl = minioWrapper.buildThumbPreviewUrl(fileFlow, 30, TimeUnit.MINUTES);
        }

        // 获取图片下载链接
        downloadUrl = minioWrapper.buildDownloadUrl(fileFlow, 1, TimeUnit.DAYS);

        FileUploadDto fileUploadDto = new FileUploadDto();
        fileUploadDto.setFileNo(fileNo)
                .setIsImage(isImage ? CommonEnum.ZERO.getValue() : CommonEnum.ONE.getValue())
                .setViewUrl(viewUrl)
                .setThumbViewUrl(thumbViewUrl)
                .setDownloadUrl(downloadUrl);
        return fileUploadDto;
    }

    /**
     * 大文件合并
     *
     * @param mergeVo 大文件合并参数对象
     * @return 结果
     */
    public FileUploadDto bigFileMerge(BigFileMergeVo mergeVo) {

        String fileHash = mergeVo.getFileHash();
        String fileName = mergeVo.getFileName();
        Long fileSize = mergeVo.getFileSize();
        String fileType = mergeVo.getFileType();

        /*
            获取切片文件上传记录
         */
        List<FileChunkFlowEntity> fileChunkFlowList = fileChunkFlowService.findList(Collections.singletonList(fileHash));
        if (fileChunkFlowList.isEmpty()) {
            log.info("切片文件上传记录不存在");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "切片文件上传记录不存在");
        }

        StringBuilder sb = new StringBuilder();
        for (FileChunkFlowEntity fileChunkFlow : fileChunkFlowList) {
            sb.append(fileChunkFlow.getChunkHash());
        }

        String _fileHash = ShaUtils.encodeSha256(sb.toString());
        if (!fileHash.equals(_fileHash)) {
            log.info("大文件哈希值校验失败");
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), "大文件哈希值对比不一致，请确认后再上传");
        }

        /*
            minio合并切片文件
         */
        Long fileNo = minioWrapper.mergeChunkFile(fileHash, fileName, fileSize, fileType, fileChunkFlowList);

        // 判断文件是否是图片
        boolean isImage = minioWrapper.isImage(fileType);
        String viewUrl = StringUtils.EMPTY;
        String thumbViewUrl = StringUtils.EMPTY;
        String downloadUrl;

        if (isImage) {
            // 获取图片预览链接
            viewUrl = minioWrapper.buildPreviewUrl(fileNo, 30, TimeUnit.MINUTES);
            // 获取图片缩略图预览链接
            thumbViewUrl = minioWrapper.buildThumbPreviewUrl(fileNo, 30, TimeUnit.MINUTES);
        }

        // 获取图片下载链接
        downloadUrl = minioWrapper.buildDownloadUrl(fileNo, 1, TimeUnit.DAYS);

        FileUploadDto fileUploadDto = new FileUploadDto();
        fileUploadDto.setFileNo(fileNo)
                .setIsImage(isImage ? CommonEnum.ZERO.getValue() : CommonEnum.ONE.getValue())
                .setViewUrl(viewUrl)
                .setThumbViewUrl(thumbViewUrl)
                .setDownloadUrl(downloadUrl);
        return fileUploadDto;
    }

    public Long zipFile(ZipFileVo zipFileVo) {

        List<Long> fileNoList = zipFileVo.getFileNoList();
        String zipFileName = zipFileVo.getZipFileName();

        // 获取文件后缀
        String suffix = zipFileName.substring(zipFileName.lastIndexOf(".") + 1);
        log.info("文件后缀：{}", suffix);

        // 判断文件后缀是否压缩文件的后缀
        List<MultiDictEntity> multiDictList = multiDictHelper.getList(MultiDictEnum.ZIP);
        boolean bool = multiDictList.stream()
                .noneMatch(multiDict -> multiDict.getValue().equals(suffix));
        if (bool) {
            log.info("文件后缀不是配置值");
            String message = "当前系统不支持" + suffix + "后缀压缩文件";
            throw new CzzException(ResultEnum.REQUEST_FAIL.getCode(), message);
        }


        /*
            文件压缩
         */
        return minioWrapper.zipFile(fileNoList, zipFileName);
    }
}
